package com.fanrui.code.gate.server;

import java.lang.reflect.Field;
import java.net.InetSocketAddress;
import java.util.Properties;
import java.util.Scanner;

import org.apache.commons.lang3.StringUtils;

import com.fanrui.code.common.annotation.Config;
import com.fanrui.code.gate.client.GameServerClient;
import com.fanrui.code.gate.core.GateServerHandler;
import com.fanrui.code.gate.core.PlayerSession;
import com.fanrui.code.gate.core.SessionCleaner;
import com.fanrui.code.socket.netty.core.FieldPrepender;
import com.fanrui.code.socket.netty.core.FrameDecoder;
import com.fanrui.code.socket.netty.core.InboundExceptionLogHandler;
import com.fanrui.code.socket.netty.core.OutboundExceptionLogHandler;
import com.fanrui.code.socket.netty.server.SocketServer;
import com.fanrui.code.thread.LogExceptionRunnable;
import com.fanrui.code.util.core.PropertiesUtil;
import com.fanrui.code.util.core.ThreadManager;
import com.fanrui.code.util.core.VersionUtil;
import com.fanrui.code.util.logger.Stdout;
import com.fanrui.proto.cmd.CmdWrapper;

import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.protobuf.ProtobufDecoder;
import io.netty.handler.codec.protobuf.ProtobufEncoder;

public class GateServerEntery {
	/**
	 * protobuf服务器的ip地址
	 */
	@Config
	public String protobufCommandServerIP;

	/**
	 * protobuf服务器的端口号
	 */
	@Config
	public Integer protobufCommandServerPort;


	/**
	 * 服务器使用的线程个数
	 */
	@Config
	public Integer mainThreads;

	/**
	 * 服务器版本号
	 */
	@Config
	public String versionMin;
	/**
	 * 服务器版本号
	 */
	@Config
	public String versionMax;

	/**
	 * 是否为debug模式
	 */
	@Config
	public Boolean isDebug;

	
	

	@Config
	public String gamerServerIP;
	@Config
	public Integer gamerServerPort;
	@Config
	public Integer timeOut_Time;
	
	@Config
	public Integer timeOut_check_interval;
	private final static String CONFIGNAME = "GateServer.properties";
	protected SocketServer socketServer;

	public GateServerEntery() {
		Stdout.GET().info("---------------------服务器启动中---------------------");
		Stdout.GET().info("读取配置文件...");
		if (CONFIGNAME == null || StringUtils.isBlank(CONFIGNAME)) {
			Stdout.GET().error("服务器启动失败， 配置文件名无效！！！");
			return;
		}
		// 设置配置文件
		Properties properties = PropertiesUtil.getPropertiesAddDir(CONFIGNAME);
		if (properties == null) {
			Stdout.GET().error("服务器启动失败， 配置文件文件无效！！");
			return;
		}

		// 初始化配置
		initConfig(properties);

		Stdout.GET().info("配置读取完毕！！！");
		// 初始化线程池
		if (mainThreads == null) {
			mainThreads = Runtime.getRuntime().availableProcessors();
			Stdout.GET().info(String.format("mainThreads 配置为null 服务器主线程数被设置成默认值:%d", mainThreads));
		} else {
			Stdout.GET().info(String.format("服务器主线程数:%d", mainThreads));
		}
		ThreadManager.init(getClass().getSimpleName(), mainThreads);
		GameServerClient.getInstance().load(gamerServerIP, gamerServerPort);
		startCommandServer();
		startProtoBufServer();
		VersionUtil.getInstance().init(versionMin, versionMax);
		SessionCleaner.start(timeOut_Time, timeOut_check_interval);
	}

	/**
	 * 启动一个命令服务器
	 */
	private void startCommandServer() {
		
		try {
			// 输入指令的服务器
			ThreadManager.getInstance().execute(new LogExceptionRunnable() {
				@Override
				protected void logExceptionRun() throws Exception {
					Scanner scanner = new Scanner(System.in);
					while (true) {
						String command = scanner.nextLine();
						
						if ("exit".equals(command)) {
							scanner.close();
						}
					}
				}
			});
		} catch (Exception e) {
			e.printStackTrace();
		}
		System.out.println("输入命令服务器创建成功！！！");
	}

	/**
	 * 启动protobuf服务器
	 */

	protected void startProtoBufServer() {
		if (protobufCommandServerIP == null || StringUtils.isBlank(protobufCommandServerIP)) {
			System.err.println("创建protobuf命令服务器失败 protobufCommandServerIP的值无效 ：" + protobufCommandServerIP);
			return;
		}
		if (protobufCommandServerPort == null || protobufCommandServerPort <= 0) {
			System.err.println("创建protobuf命令服务器失败 protobufCommandServerPort的值无效 ：" + protobufCommandServerPort);
			return;
		}
		try {

			socketServer = new SocketServer(protobufCommandServerIP, protobufCommandServerPort) {
				@Override
				protected ChannelInitializer<SocketChannel> getChannelInitializer() {
					return getChannel();
				}
			};

			ThreadManager.getInstance().execute(new LogExceptionRunnable() {
				@Override
				protected void logExceptionRun() throws Exception {
					socketServer.startUntilClose();
				}
			});
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	public ChannelInitializer<SocketChannel> getChannel() {

		return new ChannelInitializer<SocketChannel>() {

			@Override
			protected void initChannel(SocketChannel ch) throws Exception {
				ch.pipeline()
						// .addLast("log", new LoggingHandler())
						.addLast("frame", new FrameDecoder()).addLast("field", new FieldPrepender())
						.addLast("decoder", new ProtobufDecoder(CmdWrapper.getDefaultInstance()))
						.addLast("encoder", new ProtobufEncoder())
						.addLast("clientHandler", new SimpleChannelInboundHandler<CmdWrapper>() {

							@Override
							protected void channelRead0(ChannelHandlerContext channel, CmdWrapper msg)
									throws Exception {
								String ip = ((InetSocketAddress) channel.channel().remoteAddress()).getAddress()
										.getHostAddress();
								if(!PlayerSession.filterList.contains(msg.getCmdType())){
									Stdout.GET().info("ip:{}收到消息:{},",ip,msg.getCmdType());
								}
								
								GateServerHandler.getInstance().receiveMessage(channel.channel(), msg);
							}

							@Override
							public void channelActive(ChannelHandlerContext ctx) throws Exception {
								super.channelActive(ctx);
								String ip = ((InetSocketAddress) ctx.channel().remoteAddress()).getAddress()
										.getHostAddress();
								Stdout.GET().info("IP:{}建立了连接", ip);
							}

							@Override
							public void channelInactive(ChannelHandlerContext ctx) throws Exception {
								super.channelInactive(ctx);
								String ip = ((InetSocketAddress) ctx.channel().remoteAddress()).getAddress()
										.getHostAddress();
								Stdout.GET().info("IP:{}断开了连接", ip);
								GateServerHandler.getInstance().clientOffline(ctx.channel());
							}

							@Override
							public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
								super.exceptionCaught(ctx, cause);
							}
						}).addLast("inboundException", new InboundExceptionLogHandler())
						.addLast("outboundException", new OutboundExceptionLogHandler());
			}
		};
	}

	

	/**
	 * 使用反射加載配置
	 */
	private void initConfig(Properties properties) {
		Field[] fields = this.getClass().getFields();
		for (Field field : fields) {
			Config config = field.getAnnotation(Config.class);
			if (config != null) {
				try {
					if (field.getType() == Integer.class) {
						field.setAccessible(true);
						field.set(this, getIntegerParameter(properties, field.getName()));
					}
					if (field.getType() == String.class) {
						field.setAccessible(true);
						field.set(this, getStringParameter(properties, field.getName()));
					}
					if (field.getType() == Boolean.class) {
						field.setAccessible(true);
						field.set(this, getBooleanParameter(properties, field.getName()));
					}
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		}
	}

	/**
	 * 获取boolean类型的参数
	 */
	private Boolean getBooleanParameter(Properties properties, String parameterName) {
		if (properties != null) {
			String parameterValue = properties.getProperty(parameterName);
			if (parameterValue != null && StringUtils.isNoneBlank(parameterName)) {
				return new Boolean(parameterValue);
			}
		}
		return null;
	}

	/**
	 * 获取int类型的参数
	 */
	private Integer getIntegerParameter(Properties properties, String parameterName) {
		if (properties != null) {
			String parameterValue = properties.getProperty(parameterName);
			if (parameterValue != null && StringUtils.isNoneBlank(parameterName)) {
				return Integer.parseInt(parameterValue);
			}
		}
		return null;
	}

	/**
	 * 获取String类型的参数
	 */
	private String getStringParameter(Properties properties, String parameterName) {
		if (properties != null) {
			return properties.getProperty(parameterName);
		}
		return null;
	}

}